<!-- build time:Wed Dec 30 2020 09:55:47 GMT+0800 (中国标准时间) --><!DOCTYPE html><html lang="zh-CN"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,maximum-scale=2"><meta name="theme-color" content="#222"><meta name="generator" content="Hexo 4.2.1"><link rel="apple-touch-icon" sizes="180x180" href="/images/apple-touch-icon-next.png"><link rel="icon" type="image/png" sizes="32x32" href="/images/favicon-32x32-next.png"><link rel="icon" type="image/png" sizes="16x16" href="/images/favicon-16x16-next.png"><link rel="mask-icon" href="/images/logo.svg" color="#222"><link rel="stylesheet" href="/css/main.css"><link rel="stylesheet" href="/lib/font-awesome/css/all.min.css"><link rel="stylesheet" href="//cdn.jsdelivr.net/gh/fancyapps/fancybox@3/dist/jquery.fancybox.min.css"><script id="hexo-configurations">var NexT=window.NexT||{},CONFIG={hostname:"allens.icu",root:"/",scheme:"Pisces",version:"7.8.0",exturl:!1,sidebar:{position:"left",display:"post",padding:18,offset:12,onmobile:!1},copycode:{enable:!0,show_result:!0,style:"mac"},back2top:{enable:!0,sidebar:!0,scrollpercent:!0},bookmark:{enable:!1,color:"#222",save:"auto"},fancybox:!0,mediumzoom:!1,lazyload:!0,pangu:!1,comments:{style:"tabs",active:null,storage:!0,lazyload:!1,nav:null},algolia:{hits:{per_page:10},labels:{input_placeholder:"Search for Posts",hits_empty:"We didn't find any results for the search: ${query}",hits_stats:"${hits} results found in ${time} ms"}},localsearch:{enable:!0,trigger:"auto",top_n_per_article:1,unescape:!1,preload:!1},motion:{enable:!0,async:!1,transition:{post_block:"fadeIn",post_header:"slideDownIn",post_body:"slideDownIn",coll_header:"slideLeftIn",sidebar:"slideUpIn"}},path:"search.xml"}</script><meta name="description" content="Handler 老生常谈的问题了，非常建议看一下Handler 的源码。刚入行的时候，大佬们就说 阅读源码 是进步很快的方式。"><meta property="og:type" content="article"><meta property="og:title" content="Android Handler 浅析"><meta property="og:url" content="https://allens.icu/posts/a9cacf3c/index.html"><meta property="og:site_name" content="在雨季等你"><meta property="og:description" content="Handler 老生常谈的问题了，非常建议看一下Handler 的源码。刚入行的时候，大佬们就说 阅读源码 是进步很快的方式。"><meta property="og:locale" content="zh_CN"><meta property="og:image" content="https://gitee.com/_Allens/BlogImage/raw/master/image/20201015134943.png"><meta property="og:image" content="https://gitee.com/_Allens/BlogImage/raw/master/image/20201015134633.png"><meta property="og:image" content="https://gitee.com/_Allens/BlogImage/raw/master/image/20201015134702.png"><meta property="og:image" content="https://gitee.com/_Allens/BlogImage/raw/master/image/20201111104647.gif"><meta property="og:image" content="https://gitee.com/_Allens/BlogImage/raw/master/image/20201111105240.gif"><meta property="og:image" content="https://gitee.com/_Allens/BlogImage/raw/master/image/20201111105657.gif"><meta property="article:published_time" content="2020-10-15T05:42:49.000Z"><meta property="article:modified_time" content="2020-11-12T11:47:26.192Z"><meta property="article:author" content="在雨季等你"><meta property="article:tag" content="Android系统"><meta name="twitter:card" content="summary"><meta name="twitter:image" content="https://gitee.com/_Allens/BlogImage/raw/master/image/20201015134943.png"><link rel="canonical" href="https://allens.icu/posts/a9cacf3c/"><script id="page-configurations">CONFIG.page={sidebar:"",isHome:!1,isPost:!0,lang:"zh-CN"}</script><title>Android Handler 浅析 | 在雨季等你</title><noscript><style>.sidebar-inner,.use-motion .brand,.use-motion .collection-header,.use-motion .comments,.use-motion .menu-item,.use-motion .pagination,.use-motion .post-block,.use-motion .post-body,.use-motion .post-header{opacity:initial}.use-motion .site-subtitle,.use-motion .site-title{opacity:initial;top:initial}.use-motion .logo-line-before i{left:initial}.use-motion .logo-line-after i{right:initial}</style></noscript></head><body itemscope itemtype="http://schema.org/WebPage"><div class="container use-motion"><div class="headband"></div><header class="header" itemscope itemtype="http://schema.org/WPHeader"><div class="header-inner"><div class="site-brand-container"><div class="site-nav-toggle"><div class="toggle" aria-label="切换导航栏"><span class="toggle-line toggle-line-first"></span> <span class="toggle-line toggle-line-middle"></span> <span class="toggle-line toggle-line-last"></span></div></div><div class="site-meta"><a href="/" class="brand" rel="start"><span class="logo-line-before"><i></i></span><h1 class="site-title">在雨季等你</h1><span class="logo-line-after"><i></i></span></a></div><div class="site-nav-right"><div class="toggle popup-trigger"><i class="fa fa-search fa-fw fa-lg"></i></div></div></div><nav class="site-nav"><ul id="menu" class="main-menu menu"><li class="menu-item menu-item-home"><a href="/" rel="section"><i class="home fa-fw"></i>首页</a></li><li class="menu-item menu-item-about"><a href="/about/" rel="section"><i class="user fa-fw"></i>关于</a></li><li class="menu-item menu-item-tags"><a href="/tags/" rel="section"><i class="tags fa-fw"></i>标签<span class="badge">6</span></a></li><li class="menu-item menu-item-categories"><a href="/categories/" rel="section"><i class="th fa-fw"></i>分类<span class="badge">7</span></a></li><li class="menu-item menu-item-archives"><a href="/archives/" rel="section"><i class="archive fa-fw"></i>归档<span class="badge">23</span></a></li><li class="menu-item menu-item-search"><a role="button" class="popup-trigger"><i class="fa fa-search fa-fw"></i>搜索</a></li></ul></nav><div class="search-pop-overlay"><div class="popup search-popup"><div class="search-header"><span class="search-icon"><i class="fa fa-search"></i></span><div class="search-input-container"><input autocomplete="off" autocapitalize="off" placeholder="搜索..." spellcheck="false" type="search" class="search-input"></div><span class="popup-btn-close"><i class="fa fa-times-circle"></i></span></div><div id="search-result"><div id="no-result"><i class="fa fa-spinner fa-pulse fa-5x fa-fw"></i></div></div></div></div></div></header><main class="main"><div class="main-inner"><div class="content-wrap"><div class="content post posts-expand"><article itemscope itemtype="http://schema.org/Article" class="post-block" lang="zh-CN"><link itemprop="mainEntityOfPage" href="https://allens.icu/posts/a9cacf3c/"><span hidden itemprop="author" itemscope itemtype="http://schema.org/Person"><meta itemprop="image" content="/images/heard.jpg"><meta itemprop="name" content="在雨季等你"><meta itemprop="description" content="分享是进步最快的方式"></span><span hidden itemprop="publisher" itemscope itemtype="http://schema.org/Organization"><meta itemprop="name" content="在雨季等你"></span><header class="post-header"><h1 class="post-title" itemprop="name headline">Android Handler 浅析</h1><div class="post-meta"><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar"></i> </span><span class="post-meta-item-text">发表于</span> <time title="创建时间：2020-10-15 13:42:49" itemprop="dateCreated datePublished" datetime="2020-10-15T13:42:49+08:00">2020-10-15</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-calendar-check"></i> </span><span class="post-meta-item-text">更新于</span> <time title="修改时间：2020-11-12 19:47:26" itemprop="dateModified" datetime="2020-11-12T19:47:26+08:00">2020-11-12</time> </span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-folder"></i> </span><span class="post-meta-item-text">分类于</span> <span itemprop="about" itemscope itemtype="http://schema.org/Thing"><a href="/categories/Android/" itemprop="url" rel="index"><span itemprop="name">Android</span></a> </span></span><span class="post-meta-item"><span class="post-meta-item-icon"><i class="far fa-comment"></i> </span><span class="post-meta-item-text">评论数：</span> <a title="valine" href="/posts/a9cacf3c/#valine-comments" itemprop="discussionUrl"><span class="post-comments-count valine-comment-count" data-xid="/posts/a9cacf3c/" itemprop="commentCount"></span></a></span><br><span class="post-meta-item" title="本文字数"><span class="post-meta-item-icon"><i class="far fa-file-word"></i> </span><span class="post-meta-item-text">本文字数：</span> <span>16k</span> </span><span class="post-meta-item" title="阅读时长"><span class="post-meta-item-icon"><i class="far fa-clock"></i> </span><span class="post-meta-item-text">阅读时长 &asymp;</span> <span>14 分钟</span></span></div></header><div class="post-body" itemprop="articleBody"><p>Handler 老生常谈的问题了，非常建议看一下Handler 的源码。刚入行的时候，大佬们就说 阅读源码 是进步很快的方式。</p><a id="more"></a><h1 id="Handler的基本原理"><a href="#Handler的基本原理" class="headerlink" title="Handler的基本原理"></a>Handler的基本原理</h1><p>Handler 的 重要组成部分</p><ul><li>Message 消息</li><li>MessageQueue 消息队列</li><li>Lopper 负责处理MessageQueue中的消息</li></ul><p><img data-src="https://gitee.com/_Allens/BlogImage/raw/master/image/20201015134943.png" alt="图片来源网络"></p><h1 id="消息是如何添加到队列的"><a href="#消息是如何添加到队列的" class="headerlink" title="消息是如何添加到队列的"></a>消息是如何添加到队列的</h1><p>对照着上面的大的逻辑图，我们深入一下，看一下，一个消息 是如何被发送到 MessageQueue 又是如何被 Lopper 处理的</p><p>handler 发送一个message 的方法如下图所示</p><p><img data-src="https://gitee.com/_Allens/BlogImage/raw/master/image/20201015134633.png" alt=""></p><p><img data-src="https://gitee.com/_Allens/BlogImage/raw/master/image/20201015134702.png" alt=""></p><p>而这些方法最终都会执行 Handler 中的 enqueueMessage 方法，我们看一下 enqueueMessage 方法做了什么</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Handler</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">enqueueMessage</span><span class="params">(@NonNull MessageQueue queue, @NonNull Message msg,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">long</span> uptimeMillis)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="comment">//这里执行MessageQueue的 enqueueMessage</span></span><br><span class="line">    <span class="keyword">return</span> queue.enqueueMessage(msg, uptimeMillis);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="消息队列如何将消息排序"><a href="#消息队列如何将消息排序" class="headerlink" title="消息队列如何将消息排序"></a>消息队列如何将消息排序</h1><p>MessageQueue 收到 消息以后，会根据时间进行排列</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">boolean</span> <span class="title">enqueueMessage</span><span class="params">(Message msg, <span class="keyword">long</span> when)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (msg.target == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalArgumentException(<span class="string">"Message must have a target."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">if</span> (msg.isInUse()) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(msg + <span class="string">" This message is already in use."</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (mQuitting) &#123;</span><br><span class="line">            IllegalStateException e = <span class="keyword">new</span> IllegalStateException(</span><br><span class="line">                    msg.target + <span class="string">" sending message to a Handler on a dead thread"</span>);</span><br><span class="line">            Log.w(TAG, e.getMessage(), e);</span><br><span class="line">            msg.recycle();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">false</span>;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        msg.markInUse();</span><br><span class="line">        msg.when = when;</span><br><span class="line">        <span class="comment">//step1 获取头部的message</span></span><br><span class="line">        Message p = mMessages;</span><br><span class="line">        <span class="keyword">boolean</span> needWake;</span><br><span class="line">        <span class="comment">//step2 头部的message 和 当前的message 对比，如果头部的message 执行时间要 小于 当前message 的时候</span></span><br><span class="line">        <span class="comment">//那么就先执行当前的message</span></span><br><span class="line">        <span class="keyword">if</span> (p == <span class="keyword">null</span> || when == <span class="number">0</span> || when &lt; p.when) &#123;</span><br><span class="line">            msg.next = p;</span><br><span class="line">            <span class="comment">//头部的message 就变成了 当前的message</span></span><br><span class="line">            mMessages = msg;</span><br><span class="line">            needWake = mBlocked;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//step3 将当前消息 插入到 中间排队</span></span><br><span class="line">            needWake = mBlocked &amp;&amp; p.target == <span class="keyword">null</span> &amp;&amp; msg.isAsynchronous();</span><br><span class="line">            Message prev;</span><br><span class="line">            <span class="comment">//根据时间进行排序</span></span><br><span class="line">            <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">                prev = p;</span><br><span class="line">                p = p.next;</span><br><span class="line">                <span class="keyword">if</span> (p == <span class="keyword">null</span> || when &lt; p.when) &#123;</span><br><span class="line">                    <span class="keyword">break</span>;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">if</span> (needWake &amp;&amp; p.isAsynchronous()) &#123;</span><br><span class="line">                    needWake = <span class="keyword">false</span>;</span><br><span class="line">                &#125;</span><br><span class="line">            &#125;</span><br><span class="line">            msg.next = p; <span class="comment">// invariant: p == prev.next</span></span><br><span class="line">            prev.next = msg;</span><br><span class="line">        &#125;</span><br><span class="line"></span><br><span class="line">        <span class="comment">// We can assume mPtr != 0 because mQuitting is false.</span></span><br><span class="line">        <span class="keyword">if</span> (needWake) &#123;</span><br><span class="line">            nativeWake(mPtr);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">true</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="Handler的消息队列在哪创建的"><a href="#Handler的消息队列在哪创建的" class="headerlink" title="Handler的消息队列在哪创建的"></a>Handler的消息队列在哪创建的</h1><p>回到创建Handler的地方，他的构造方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Handler</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Handler</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">this</span>(<span class="keyword">null</span>, <span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Handler</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Handler</span><span class="params">(Callback callback, <span class="keyword">boolean</span> async)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="comment">//获取当前的looper</span></span><br><span class="line">    mLooper = Looper.myLooper();</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="comment">//获取looper 的 MessageQueue</span></span><br><span class="line">    mQueue = mLooper.mQueue;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="keyword">final</span> MessageQueue mQueue;</span><br><span class="line"></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="title">Looper</span><span class="params">(<span class="keyword">boolean</span> quitAllowed)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//在这里创建了一个 MessageQueue</span></span><br><span class="line">    mQueue = <span class="keyword">new</span> MessageQueue(quitAllowed);</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>可以看到 Handler其实是拿着Looper 的MessageQueue当做自己的MessageQueue</p><h1 id="Loope有什么作用"><a href="#Loope有什么作用" class="headerlink" title="Loope有什么作用"></a>Loope有什么作用</h1><p>消息被有序的添加到了消息队列中，而Looper就是负责将消息从消息队列中取出。当执行Looper的loop()方法，Looper会从消息队列中取出消息，然后交给handler的dispatchMessage去处理消息</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">loop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">//从消息队列中获取消息</span></span><br><span class="line">        Message msg = queue.next(); <span class="comment">// might block</span></span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//msg.traget 就是Handler </span></span><br><span class="line">                <span class="comment">//使用 Handler 的  dispatchMessage() 处理消息</span></span><br><span class="line">                msg.target.dispatchMessage(msg);</span><br><span class="line">                <span class="comment">//...</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception exception) &#123;</span><br><span class="line">            <span class="comment">//...</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="一个线程有几个Looper"><a href="#一个线程有几个Looper" class="headerlink" title="一个线程有几个Looper"></a>一个线程有几个Looper</h1><p>要想知道有几个Lopper,肯定要先知道Looper在哪里创建。Looper有一个prepare方法</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    prepare(<span class="keyword">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在这里会创建一个新的Looper 并且设置到了ThreadLocal</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(<span class="keyword">boolean</span> quitAllowed)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//通过 sThreadLocal get 检查是否已经有 looper  </span></span><br><span class="line">    <span class="keyword">if</span> (sThreadLocal.get() != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//如果已经有了 就抛出异常</span></span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> RuntimeException(<span class="string">"Only one Looper may be created per thread"</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//没有的话 就设置一个新的Looper</span></span><br><span class="line">    sThreadLocal.set(<span class="keyword">new</span> Looper(quitAllowed));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在ThreadLocal可以看到是以map的形式去保存，保证了一个线程只有一个map,又将looper和ThreadLocal进行绑定</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ThreadLocal</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">set</span><span class="params">(T value)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//获取当前线程</span></span><br><span class="line">    Thread t = Thread.currentThread();</span><br><span class="line">    <span class="comment">//获取 ThreadLocalMap </span></span><br><span class="line">    ThreadLocalMap map = getMap(t);</span><br><span class="line">    <span class="comment">//有的话 就将当前的 ThreadLocal 和 Looper 绑定在一起，</span></span><br><span class="line">    <span class="keyword">if</span> (map != <span class="keyword">null</span>)</span><br><span class="line">        <span class="comment">//set 以后 在上面  sThreadLocal.get() 就不会在为null了</span></span><br><span class="line">        map.set(<span class="keyword">this</span>, value);</span><br><span class="line">    <span class="keyword">else</span></span><br><span class="line">        <span class="comment">//没有的话 创建一个 ThreadLocalMap 在绑定在一起</span></span><br><span class="line">        createMap(t, value);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>看到Looper中的 sThreadLocal</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="keyword">static</span> <span class="keyword">final</span> ThreadLocal&lt;Looper&gt; sThreadLocal = <span class="keyword">new</span> ThreadLocal&lt;Looper&gt;();</span><br></pre></td></tr></table></figure><p>他是一个静态的 final 保证了 一个Looper只有一个 sThreadLocal</p><p>最终保证了，一个线程只有一个Looper</p><h1 id="主线程什么时候执行preapre"><a href="#主线程什么时候执行preapre" class="headerlink" title="主线程什么时候执行preapre"></a>主线程什么时候执行preapre</h1><p>想要使用Looper，肯定需要先prepare 去创建一个Looper,那么主线程如何创建Looper的呢？我们知道 java 程序的入口是 main 方法， 对于Android来说，其实也有一个main 方法，他的位置在 <code>ActivityThread</code></p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ActivityThread</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">main</span><span class="params">(String[] args)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="comment">//可以看到在这里 程序启动以后，Android 系统帮我们将主线程的Looper prepare</span></span><br><span class="line">    Looper.prepareMainLooper();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="comment">//然后帮助我们启动了 loop</span></span><br><span class="line">    Looper.loop();</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="Handler内存泄露"><a href="#Handler内存泄露" class="headerlink" title="Handler内存泄露"></a>Handler内存泄露</h1><p>Handler为什么会有可能导致内存泄露？ 我们知道 内部类会持有外部类的引用,当我们做一个延时任务，延时10S,然后在10S内退出Activity,在我们sendMessage的时候,handler对象被传递给msg 如👇所示，然后被存放在MessageQueue中。在这10S内，即使Activity销毁了，但是引用关系依然被保存在MessageQueue中,那么即使Activity销毁了,他的对象依然不会被GC销毁，因为他依然被引用。就导致内存未被回收。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Handler</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">enqueueMessage</span><span class="params">(@NonNull MessageQueue queue, @NonNull Message msg,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">long</span> uptimeMillis)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//这里 将 handler 本身的对象 传给 msg 的target</span></span><br><span class="line">    msg.target = <span class="keyword">this</span>;</span><br><span class="line">   <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>那么如何处理Handler内存泄露呢</p><p>1.将Handler改成静态类。原因是因为静态类不会持有外部类的引用<br>2.继承Handler,将Activity作为弱引用使用<br>3.在界面退出的时候,调用Handler的removeMessages方法</p><h1 id="消息队列没有消息时Handler如何挂起"><a href="#消息队列没有消息时Handler如何挂起" class="headerlink" title="消息队列没有消息时Handler如何挂起"></a>消息队列没有消息时Handler如何挂起</h1><p>Looper从MessageQueue中获取message，当获取不到message的时候，会将 nextPollTimeoutMillis置成-1，然后进入下次循环，当执行nativePollOnce方法时候，如果nextPollTimeoutMillis==-1那么就会执行Linux的epoll机制，让线程处于挂起状态，阻塞线程。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function">Message <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">//step3: nextPollTimeoutMillis == -1 执行native 函数，</span></span><br><span class="line">        <span class="comment">//执行 linux epoll 机制，线程处于等待状态，线程挂起</span></span><br><span class="line">        nativePollOnce(ptr, nextPollTimeoutMillis);</span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">            <span class="comment">//...</span></span><br><span class="line">            <span class="keyword">if</span> (msg != <span class="keyword">null</span>) &#123;</span><br><span class="line">                </span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                <span class="comment">// step1:如果没有消息  nextPollTimeoutMillis 变成-1</span></span><br><span class="line">                nextPollTimeoutMillis = -<span class="number">1</span>;</span><br><span class="line">            &#125;</span><br><span class="line"></span><br><span class="line">            <span class="keyword">if</span> (pendingIdleHandlerCount &lt;= <span class="number">0</span>) &#123;</span><br><span class="line">                <span class="comment">// step2:跳出循环 进入下一次循环</span></span><br><span class="line">                mBlocked = <span class="keyword">true</span>;</span><br><span class="line">                <span class="keyword">continue</span>;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">loop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">//step4:这里也就挂起了</span></span><br><span class="line">        Message msg = queue.next(); <span class="comment">// might block</span></span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="Handler如何退出"><a href="#Handler如何退出" class="headerlink" title="Handler如何退出"></a>Handler如何退出</h1><p>使用looper去执行quit方法退出</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><span class="line">handler.looper.quit()</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">quit</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    mQueue.quit(<span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">quitSafely</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    mQueue.quit(<span class="keyword">true</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">quit</span><span class="params">(<span class="keyword">boolean</span> safe)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">if</span> (!mQuitAllowed) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Main thread not allowed to quit."</span>);</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">        <span class="keyword">if</span> (mQuitting) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//step1:将mQuitting 变量变成true</span></span><br><span class="line">        mQuitting = <span class="keyword">true</span>;</span><br><span class="line">        <span class="comment">//step2:删除所有的消息</span></span><br><span class="line">        <span class="keyword">if</span> (safe) &#123;</span><br><span class="line">            removeAllFutureMessagesLocked();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            removeAllMessagesLocked();</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//step3:唤醒线程</span></span><br><span class="line">        nativeWake(mPtr);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function">Message <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">//step4:线程被唤醒。继续执行</span></span><br><span class="line">        nativePollOnce(ptr, nextPollTimeoutMillis);</span><br><span class="line">        </span><br><span class="line">        <span class="comment">//step5:检查到状态是 true 返回null 出去</span></span><br><span class="line">        <span class="keyword">if</span> (mQuitting) &#123;</span><br><span class="line">            dispose();</span><br><span class="line">            <span class="keyword">return</span> <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">loop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">//step6:这里也被唤醒获取到message == null</span></span><br><span class="line">        Message msg = queue.next(); <span class="comment">// might block</span></span><br><span class="line">        <span class="comment">//step7:最终在这里🔚循环</span></span><br><span class="line">        <span class="keyword">if</span> (msg == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">return</span>;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>总结</p></blockquote><p>Looper会先将消息队列中的消息全部清空，然后使用nativeWake的native方法唤醒线程，在上面我们介绍了，当消息队列中没有消息的时候，线程会挂起，处于等待状态，当我们唤醒以后，Looper的loop方法会继续执行下去，然后从MessageQueue中获取到一个null的Message,最终将Looper的loop()方法退出</p><h1 id="主线程能够Quit么？"><a href="#主线程能够Quit么？" class="headerlink" title="主线程能够Quit么？"></a>主线程能够Quit么？</h1><p>我们知道了 主线程是在ActivityThread的main方法中执行了<code>Looper.prepareMainLooper()</code>创建的Looper</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="meta">@Deprecated</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepareMainLooper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//step1: 注意看这里是一个false</span></span><br><span class="line">    prepare(<span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(<span class="keyword">boolean</span> quitAllowed)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//step2:new的Looper传入的是false</span></span><br><span class="line">    sThreadLocal.set(<span class="keyword">new</span> Looper(quitAllowed));</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="title">Looper</span><span class="params">(<span class="keyword">boolean</span> quitAllowed)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//step3:创建的MessageQueue 传入的也是false</span></span><br><span class="line">    mQueue = <span class="keyword">new</span> MessageQueue(quitAllowed);</span><br><span class="line">    mThread = Thread.currentThread();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line">MessageQueue(<span class="keyword">boolean</span> quitAllowed) &#123;</span><br><span class="line">    <span class="comment">//step4:将mQuitAllowed 变量变成了false</span></span><br><span class="line">    mQuitAllowed = quitAllowed;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">quit</span><span class="params">(<span class="keyword">boolean</span> safe)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//step5:如果是false 就是主线程 会直接抛出错误</span></span><br><span class="line">    <span class="keyword">if</span> (!mQuitAllowed) &#123;</span><br><span class="line">        <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"Main thread not allowed to quit."</span>);</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>回头在看一下 Looper的prepare方法，只有主线程可以创建一个不可以quit的MessageQueue,其他线程创建的都是可以quit的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="comment">//公开方法 prepare 传入的是true</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    prepare(<span class="keyword">true</span>);</span><br><span class="line">&#125;</span><br><span class="line"><span class="comment">//私有方法</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepare</span><span class="params">(<span class="keyword">boolean</span> quitAllowed)</span> </span></span><br><span class="line"><span class="function"></span></span><br><span class="line"><span class="function"><span class="comment">//主线程 传入的是false</span></span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">prepareMainLooper</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    prepare(<span class="keyword">false</span>);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="为什么设计主线程不能被quit"><a href="#为什么设计主线程不能被quit" class="headerlink" title="为什么设计主线程不能被quit"></a>为什么设计主线程不能被quit</h1><p>在ActivityThread中，定义了一个H的类，继承了Handler,这个H的handler执行了Android所有的主要事件，比如广播，service，Activity生命周期等都是在这里进行处理，所以不能把主线程quit</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//ActivityThread</span></span><br><span class="line"><span class="class"><span class="keyword">class</span> <span class="title">H</span> <span class="keyword">extends</span> <span class="title">Handler</span> </span>&#123;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="消息如何知道是由哪个Handler发送的？"><a href="#消息如何知道是由哪个Handler发送的？" class="headerlink" title="消息如何知道是由哪个Handler发送的？"></a>消息如何知道是由哪个Handler发送的？</h1><p>一个线程可以有多个Handler,想new几个都可以，在我们往MessageQueue中添加消息的时候，会加入一个target标记是哪个handler发送的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Handler</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">enqueueMessage</span><span class="params">(@NonNull MessageQueue queue, @NonNull Message msg,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">long</span> uptimeMillis)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//step1:在这里 就标记了是哪一个handler 发送的 </span></span><br><span class="line">    msg.target = <span class="keyword">this</span>;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Looper</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> <span class="keyword">void</span> <span class="title">loop</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">            <span class="keyword">try</span> &#123;</span><br><span class="line">                <span class="comment">//step2:这里就对应起来是哪一个handler 发送的message </span></span><br><span class="line">                msg.target.dispatchMessage(msg);</span><br><span class="line">                <span class="comment">//...</span></span><br><span class="line">        &#125; <span class="keyword">catch</span> (Exception exception) &#123;</span><br><span class="line">            <span class="comment">//...</span></span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//...</span></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="Handler如何确保线程安全的"><a href="#Handler如何确保线程安全的" class="headerlink" title="Handler如何确保线程安全的"></a>Handler如何确保线程安全的</h1><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">boolean</span> <span class="title">enqueueMessage</span><span class="params">(Message msg, <span class="keyword">long</span> when)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//step1:通过加锁的方式，保证了添加消息到消息队列的安全</span></span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function">Message <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">for</span> (;;) &#123;</span><br><span class="line">        <span class="comment">//step2:通过枷锁的方式保证了读取消息的安全</span></span><br><span class="line">        <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h1 id="Message如何复用的"><a href="#Message如何复用的" class="headerlink" title="Message如何复用的"></a>Message如何复用的</h1><p>看一下我们quit的时候，是怎么从消息队列中清空消息的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">quit</span><span class="params">(<span class="keyword">boolean</span> safe)</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">        <span class="comment">//step1: 清除所有的消息</span></span><br><span class="line">        <span class="keyword">if</span> (safe) &#123;</span><br><span class="line">            removeAllFutureMessagesLocked();</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            removeAllMessagesLocked();</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">void</span> <span class="title">removeAllMessagesLocked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    Message p = mMessages;</span><br><span class="line">    <span class="keyword">while</span> (p != <span class="keyword">null</span>) &#123;</span><br><span class="line">        Message n = p.next;</span><br><span class="line">        <span class="comment">//step2:执行message的方法</span></span><br><span class="line">        p.recycleUnchecked();</span><br><span class="line">        p = n;</span><br><span class="line">    &#125;</span><br><span class="line">    mMessages = <span class="keyword">null</span>;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Message</span></span><br><span class="line"><span class="function"><span class="keyword">void</span> <span class="title">recycleUnchecked</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//step3:将所有的变量全部清空</span></span><br><span class="line">    flags = FLAG_IN_USE;</span><br><span class="line">    what = <span class="number">0</span>;</span><br><span class="line">    arg1 = <span class="number">0</span>;</span><br><span class="line">    arg2 = <span class="number">0</span>;</span><br><span class="line">    obj = <span class="keyword">null</span>;</span><br><span class="line">    replyTo = <span class="keyword">null</span>;</span><br><span class="line">    sendingUid = UID_NONE;</span><br><span class="line">    workSourceUid = UID_NONE;</span><br><span class="line">    when = <span class="number">0</span>;</span><br><span class="line">    target = <span class="keyword">null</span>;</span><br><span class="line">    callback = <span class="keyword">null</span>;</span><br><span class="line">    data = <span class="keyword">null</span>;</span><br><span class="line"></span><br><span class="line">    <span class="keyword">synchronized</span> (sPoolSync) &#123;</span><br><span class="line">        <span class="comment">//默认50个Message</span></span><br><span class="line">        <span class="keyword">if</span> (sPoolSize &lt; MAX_POOL_SIZE) &#123;</span><br><span class="line">            <span class="comment">//step4:将已经清空状态的Message 放到一个新的链表中</span></span><br><span class="line">            next = sPool;</span><br><span class="line">            sPool = <span class="keyword">this</span>;</span><br><span class="line">            sPoolSize++;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>使用obtain方法会从之前清空状态的链表中取出一个Message去使用，减少创建Message带来的内存消耗。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Message</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">static</span> Message <span class="title">obtain</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">synchronized</span> (sPoolSync) &#123;</span><br><span class="line">        <span class="keyword">if</span> (sPool != <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="comment">//step5:从已经清空状态的链表中取出一个Message使用</span></span><br><span class="line">            Message m = sPool;</span><br><span class="line">            sPool = m.next;</span><br><span class="line">            m.next = <span class="keyword">null</span>;</span><br><span class="line">            m.flags = <span class="number">0</span>; <span class="comment">// clear in-use flag</span></span><br><span class="line">            sPoolSize--;</span><br><span class="line">            <span class="keyword">return</span> m;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> <span class="keyword">new</span> Message();</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><blockquote><p>这种设计模式称为享元设计模式</p></blockquote><h1 id="为什么主线程loop不会导致ANR"><a href="#为什么主线程loop不会导致ANR" class="headerlink" title="为什么主线程loop不会导致ANR"></a>为什么主线程loop不会导致ANR</h1><p>首先要知道ANR是怎么出现的，ANR出现的条件有两个</p><ul><li>5秒内没有响应输入的事件，触摸反馈等</li><li>广播10秒内没有执行完毕</li></ul><p>在上面我们分析知道，所有的事件都是由Handler进行分发，在主线程上，发送一个事件，这个事件耗时，将主线程的loop()给卡主，让他只能执行当前任务，不能去处理其他事件就出现了ANR</p><p>ANR的本质是由于不能及时处理消息导致的，和他的loop是没有任何关系的</p><h1 id="Handler同步屏障"><a href="#Handler同步屏障" class="headerlink" title="Handler同步屏障"></a>Handler同步屏障</h1><h2 id="同步屏障概念"><a href="#同步屏障概念" class="headerlink" title="同步屏障概念"></a>同步屏障概念</h2><p>啥叫同步屏障，字面意思，就是阻挡同步消息，那么Handler同步屏障是干啥的，没错，你没听错，就是阻挡同步消息，让异步消息过去。阻挡同步消息 这就是同步屏障</p><p>在发送消息的时候，mAsynchronous 控制着是否发送的消息是否为异步消息</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Handler</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">boolean</span> <span class="title">enqueueMessage</span><span class="params">(@NonNull MessageQueue queue, @NonNull Message msg,</span></span></span><br><span class="line"><span class="function"><span class="params">        <span class="keyword">long</span> uptimeMillis)</span> </span>&#123;</span><br><span class="line">    msg.target = <span class="keyword">this</span>;</span><br><span class="line">    msg.workSourceUid = ThreadLocalWorkSource.getUid();</span><br><span class="line"></span><br><span class="line">    <span class="comment">//如果是true 则将消息标记为异步消息</span></span><br><span class="line">    <span class="keyword">if</span> (mAsynchronous) &#123;</span><br><span class="line">        msg.setAsynchronous(<span class="keyword">true</span>);</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="keyword">return</span> queue.enqueueMessage(msg, uptimeMillis);</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><p>在Handler构造方法中，控制则是否是异步消息。但是这个方法是hide，正常我们是不能调用的</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//Handler</span></span><br><span class="line"><span class="meta">@hide</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="title">Handler</span><span class="params">(@Nullable Callback callback, <span class="keyword">boolean</span> async)</span> </span>&#123;</span><br><span class="line">    <span class="comment">//这里控制着变量</span></span><br><span class="line">    mAsynchronous = async;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="开启同步屏障"><a href="#开启同步屏障" class="headerlink" title="开启同步屏障"></a>开启同步屏障</h2><p>那么如何开启同步屏障呢,MessageQueue 中提供了一个 postSyncBarrier 方法 开启同步屏障，</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">int</span> <span class="title">postSyncBarrier</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="keyword">return</span> postSyncBarrier(SystemClock.uptimeMillis());</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">private</span> <span class="keyword">int</span> <span class="title">postSyncBarrier</span><span class="params">(<span class="keyword">long</span> when)</span> </span>&#123;</span><br><span class="line">    <span class="comment">// Enqueue a new sync barrier token.</span></span><br><span class="line">    <span class="comment">// We don't need to wake the queue because the purpose of a barrier is to stall it.</span></span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">int</span> token = mNextBarrierToken++;</span><br><span class="line">        <span class="keyword">final</span> Message msg = Message.obtain();</span><br><span class="line">        msg.markInUse();</span><br><span class="line">        <span class="comment">//👇 注意这里 开启以后没有设置target, 所以Messaged的target 是 null</span></span><br><span class="line">        msg.when = when;</span><br><span class="line">        msg.arg1 = token;</span><br><span class="line"></span><br><span class="line">        Message prev = <span class="keyword">null</span>;</span><br><span class="line">        Message p = mMessages;</span><br><span class="line">        <span class="keyword">if</span> (when != <span class="number">0</span>) &#123;</span><br><span class="line">            <span class="keyword">while</span> (p != <span class="keyword">null</span> &amp;&amp; p.when &lt;= when) &#123;</span><br><span class="line">                prev = p;</span><br><span class="line">                p = p.next;</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (prev != <span class="keyword">null</span>) &#123; <span class="comment">// invariant: p == prev.next</span></span><br><span class="line">            msg.next = p;</span><br><span class="line">            prev.next = msg;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            msg.next = p;</span><br><span class="line">            mMessages = msg;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//返回一个 token 用来取消同步屏障时候使用</span></span><br><span class="line">        <span class="keyword">return</span> token;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="同步屏障工作原理"><a href="#同步屏障工作原理" class="headerlink" title="同步屏障工作原理"></a>同步屏障工作原理</h2><p>开启以后，同步屏障如何将异步消息传递出去，将同步消息阻挡下来呢</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function">Message <span class="title">next</span><span class="params">()</span> </span>&#123;</span><br><span class="line">    <span class="comment">//...</span></span><br><span class="line">    <span class="comment">//step1:👇 看到这里 一旦收到target == null 表示同步屏障打开了</span></span><br><span class="line">    <span class="keyword">if</span> (msg != <span class="keyword">null</span> &amp;&amp; msg.target == <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="keyword">do</span> &#123;</span><br><span class="line">            prevMsg = msg;</span><br><span class="line">            msg = msg.next;</span><br><span class="line">            <span class="comment">//step2:👇 这里就做一个循环， 寻找异步消息</span></span><br><span class="line">        &#125; <span class="keyword">while</span> (msg != <span class="keyword">null</span> &amp;&amp; !msg.isAsynchronous());</span><br><span class="line">    &#125;</span><br><span class="line">    <span class="comment">//step3:当找到异步消息以后</span></span><br><span class="line">    <span class="keyword">if</span> (msg != <span class="keyword">null</span>) &#123;</span><br><span class="line">        <span class="comment">//step4:判断是否到了要执行异步消息的时间</span></span><br><span class="line">        <span class="keyword">if</span> (now &lt; msg.when) &#123;</span><br><span class="line">            <span class="comment">//如果还没到，就等nextPollTimeoutMillis</span></span><br><span class="line">            nextPollTimeoutMillis = (<span class="keyword">int</span>) Math.min(msg.when - now, Integer.MAX_VALUE);</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            <span class="comment">//如果到了执行时间 从链表中移除他</span></span><br><span class="line">            mBlocked = <span class="keyword">false</span>;</span><br><span class="line">            <span class="keyword">if</span> (prevMsg != <span class="keyword">null</span>) &#123;</span><br><span class="line">                prevMsg.next = msg.next;</span><br><span class="line">            &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">                mMessages = msg.next;</span><br><span class="line">            &#125;</span><br><span class="line">            msg.next = <span class="keyword">null</span>;</span><br><span class="line">            <span class="keyword">if</span> (DEBUG) Log.v(TAG, <span class="string">"Returning message: "</span> + msg);</span><br><span class="line">            msg.markInUse();</span><br><span class="line">            <span class="keyword">return</span> msg;</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="取消同步屏障"><a href="#取消同步屏障" class="headerlink" title="取消同步屏障"></a>取消同步屏障</h2><p>取消同步屏障以后，会唤醒线程，去处理之前未被处理的同步消息。</p><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><span class="line"><span class="comment">//MessageQueue</span></span><br><span class="line"><span class="function"><span class="keyword">public</span> <span class="keyword">void</span> <span class="title">removeSyncBarrier</span><span class="params">(<span class="keyword">int</span> token)</span> </span>&#123;</span><br><span class="line">  </span><br><span class="line">    <span class="keyword">synchronized</span> (<span class="keyword">this</span>) &#123;</span><br><span class="line">        Message prev = <span class="keyword">null</span>;</span><br><span class="line">        Message p = mMessages;</span><br><span class="line">        <span class="comment">//step1:通过token 寻找设置的同步屏障</span></span><br><span class="line">        <span class="keyword">while</span> (p != <span class="keyword">null</span> &amp;&amp; (p.target != <span class="keyword">null</span> || p.arg1 != token)) &#123;</span><br><span class="line">            prev = p;</span><br><span class="line">            p = p.next;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">if</span> (p == <span class="keyword">null</span>) &#123;</span><br><span class="line">            <span class="keyword">throw</span> <span class="keyword">new</span> IllegalStateException(<span class="string">"The specified message queue synchronization "</span></span><br><span class="line">                    + <span class="string">" barrier token has not been posted or has already been removed."</span>);</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="keyword">final</span> <span class="keyword">boolean</span> needWake;</span><br><span class="line">        <span class="comment">//step2:从链表中移除</span></span><br><span class="line">        <span class="keyword">if</span> (prev != <span class="keyword">null</span>) &#123;</span><br><span class="line">            prev.next = p.next;</span><br><span class="line">            needWake = <span class="keyword">false</span>;</span><br><span class="line">        &#125; <span class="keyword">else</span> &#123;</span><br><span class="line">            mMessages = p.next;</span><br><span class="line">            needWake = mMessages == <span class="keyword">null</span> || mMessages.target != <span class="keyword">null</span>;</span><br><span class="line">        &#125;</span><br><span class="line">        <span class="comment">//step3:将Message清空</span></span><br><span class="line">        p.recycleUnchecked();</span><br><span class="line"></span><br><span class="line">       </span><br><span class="line">        <span class="keyword">if</span> (needWake &amp;&amp; !mQuitting) &#123;</span><br><span class="line">            <span class="comment">//step4:唤醒线程</span></span><br><span class="line">            nativeWake(mPtr);</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure><h2 id="GIF演示"><a href="#GIF演示" class="headerlink" title="GIF演示"></a>GIF演示</h2><p>下面以一个简单的示例更佳直观的表现,示例分成3中情况</p><ul><li>没有启动同步屏障，发送同步消息 发送异步消息</li><li>开启同步屏障，发送同步消息 发送异步消息</li><li>开启同步屏障，发送同步消息 发送异步消息 在取消同步屏障</li></ul><blockquote><p>没有启动同步屏障，发送同步消息 发送异步消息</p></blockquote><p><img data-src="https://gitee.com/_Allens/BlogImage/raw/master/image/20201111104647.gif" alt=""></p><p>可以看到，如果不开启同步屏障，对于Handler 来说 消息都是会被发送出去</p><blockquote><p>开启同步屏障，发送同步消息 发送异步消息</p></blockquote><p><img data-src="https://gitee.com/_Allens/BlogImage/raw/master/image/20201111105240.gif" alt=""></p><p>通过对比能够发现，当开启同步屏障以后，发送的同步消息并没有打印，只有异步消息打印了，说明同步屏障确实只能够允许异步消息通过</p><blockquote><p>开启同步屏障，发送同步消息 发送异步消息 在取消同步屏障</p></blockquote><p><img data-src="https://gitee.com/_Allens/BlogImage/raw/master/image/20201111105657.gif" alt=""></p><p>当我们移除同步屏障以后，之前没有收到的同步消息，会立马同步过来</p><h2 id="演示代码"><a href="#演示代码" class="headerlink" title="演示代码"></a>演示代码</h2><figure class="highlight java"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br><span class="line">55</span><br><span class="line">56</span><br><span class="line">57</span><br><span class="line">58</span><br><span class="line">59</span><br><span class="line">60</span><br><span class="line">61</span><br><span class="line">62</span><br><span class="line">63</span><br><span class="line">64</span><br><span class="line">65</span><br><span class="line">66</span><br><span class="line">67</span><br><span class="line">68</span><br><span class="line">69</span><br><span class="line">70</span><br><span class="line">71</span><br><span class="line">72</span><br><span class="line">73</span><br><span class="line">74</span><br><span class="line">75</span><br><span class="line">76</span><br><span class="line">77</span><br><span class="line">78</span><br><span class="line">79</span><br><span class="line">80</span><br><span class="line">81</span><br><span class="line">82</span><br><span class="line">83</span><br><span class="line">84</span><br><span class="line">85</span><br><span class="line">86</span><br><span class="line">87</span><br><span class="line">88</span><br><span class="line">89</span><br><span class="line">90</span><br><span class="line">91</span><br><span class="line">92</span><br><span class="line">93</span><br><span class="line">94</span><br><span class="line">95</span><br><span class="line">96</span><br><span class="line">97</span><br><span class="line">98</span><br><span class="line">99</span><br><span class="line">100</span><br><span class="line">101</span><br><span class="line">102</span><br><span class="line">103</span><br><span class="line">104</span><br><span class="line">105</span><br><span class="line">106</span><br><span class="line">107</span><br><span class="line">108</span><br><span class="line">109</span><br><span class="line">110</span><br><span class="line">111</span><br><span class="line">112</span><br><span class="line">113</span><br><span class="line">114</span><br><span class="line">115</span><br><span class="line">116</span><br><span class="line">117</span><br><span class="line">118</span><br><span class="line">119</span><br><span class="line">120</span><br><span class="line">121</span><br><span class="line">122</span><br><span class="line">123</span><br><span class="line">124</span><br><span class="line">125</span><br><span class="line">126</span><br><span class="line">127</span><br></pre></td><td class="code"><pre><span class="line"></span><br><span class="line">class HandlerAct : AppCompatActivity() &#123;</span><br><span class="line"></span><br><span class="line">    companion object &#123;</span><br><span class="line">        <span class="keyword">const</span> val TAG = <span class="string">"handler-tag"</span></span><br><span class="line">        <span class="keyword">const</span> val MESSAGE_TYPE_SYNC = <span class="number">0x01</span></span><br><span class="line">        <span class="keyword">const</span> val MESSAGE_TYPE_ASYN = <span class="number">0x02</span></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">var</span> index = <span class="number">0</span></span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> lateinit <span class="keyword">var</span> handler :Handler</span><br><span class="line"></span><br><span class="line">    <span class="keyword">private</span> <span class="keyword">var</span> token: Int? = <span class="keyword">null</span></span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequiresApi</span>(Build.VERSION_CODES.M)</span><br><span class="line">    <span class="function">override fun <span class="title">onCreate</span><span class="params">(savedInstanceState: Bundle?)</span> </span>&#123;</span><br><span class="line">        <span class="keyword">super</span>.onCreate(savedInstanceState)</span><br><span class="line">        setContentView(R.layout.activity_handler)</span><br><span class="line">        initHandler()</span><br><span class="line"></span><br><span class="line">        linear.addView(MaterialButton(<span class="keyword">this</span>).apply &#123;</span><br><span class="line">            text = <span class="string">"插入同步屏障"</span></span><br><span class="line">            setOnClickListener &#123;</span><br><span class="line">                sendSyncBarrier()</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line">        linear.addView(MaterialButton(<span class="keyword">this</span>).apply &#123;</span><br><span class="line">            text = <span class="string">"移除屏障"</span></span><br><span class="line">            setOnClickListener &#123;</span><br><span class="line">                removeSyncBarrier()</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">        linear.addView(MaterialButton(<span class="keyword">this</span>).apply &#123;</span><br><span class="line">            text = <span class="string">"发送同步消息"</span></span><br><span class="line">            setOnClickListener &#123;</span><br><span class="line">                sendSyncMessage()</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line">        linear.addView(MaterialButton(<span class="keyword">this</span>).apply &#123;</span><br><span class="line">            text = <span class="string">"发送异步消息"</span></span><br><span class="line">            setOnClickListener &#123;</span><br><span class="line">                sendAsynMessage()</span><br><span class="line">            &#125;</span><br><span class="line">        &#125;)</span><br><span class="line"></span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> fun <span class="title">initHandler</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Thread &#123;</span><br><span class="line">            Looper.prepare()</span><br><span class="line">            handler = Handler()&#123;</span><br><span class="line">                when(it.what)&#123;</span><br><span class="line">                    MESSAGE_TYPE_SYNC -&gt; &#123;</span><br><span class="line">                        Log.i(TAG, <span class="string">"收到同步消息&lt;========== index:$&#123;it.arg1&#125;"</span>)</span><br><span class="line">                    &#125;</span><br><span class="line">                    MESSAGE_TYPE_ASYN -&gt; &#123;</span><br><span class="line">                        Log.i(TAG, <span class="string">"收到异步消息&lt;========== index:$&#123;it.arg1&#125;"</span>)</span><br><span class="line">                    &#125;</span><br><span class="line">                &#125;</span><br><span class="line">                <span class="keyword">true</span></span><br><span class="line">            &#125;</span><br><span class="line">            Looper.loop()</span><br><span class="line">        &#125;.start()</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="function"><span class="keyword">private</span> fun <span class="title">sendSyncMessage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        index++</span><br><span class="line">        Log.i(TAG, <span class="string">"插入同步消息==========&gt; index:$index"</span>)</span><br><span class="line">        val message = Message.obtain()</span><br><span class="line">        message.what = MESSAGE_TYPE_SYNC</span><br><span class="line">        message.arg1 = index</span><br><span class="line">        handler.sendMessageDelayed(message, <span class="number">1000</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//往消息队列插入异步消息</span></span><br><span class="line">    <span class="meta">@RequiresApi</span>(api = Build.VERSION_CODES.LOLLIPOP_MR1)</span><br><span class="line">    <span class="function"><span class="keyword">private</span> fun <span class="title">sendAsynMessage</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        index++</span><br><span class="line">        Log.i(TAG, <span class="string">"插入异步消息==========&gt; index:$index"</span>)</span><br><span class="line">        val message = Message.obtain()</span><br><span class="line">        message.what = MESSAGE_TYPE_ASYN</span><br><span class="line">        message.arg1 = index</span><br><span class="line">        message.isAsynchronous = <span class="keyword">true</span></span><br><span class="line">        handler.sendMessageDelayed(message, <span class="number">1000</span>)</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line"></span><br><span class="line">    <span class="meta">@RequiresApi</span>(Build.VERSION_CODES.M)</span><br><span class="line">    <span class="meta">@SuppressLint</span>(<span class="string">"DiscouragedPrivateApi"</span>)</span><br><span class="line">    <span class="function">fun <span class="title">sendSyncBarrier</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            Log.d(TAG, <span class="string">"插入同步屏障"</span>)</span><br><span class="line">            val queue: MessageQueue = handler.looper.queue</span><br><span class="line">            val method: Method = MessageQueue::class.java.getDeclaredMethod("postSyncBarrier")</span><br><span class="line">            method.isAccessible = <span class="keyword">true</span></span><br><span class="line">            token = method.invoke(queue) as Int</span><br><span class="line">            Log.d(TAG, <span class="string">"token:$token"</span>)</span><br><span class="line">        &#125; <span class="keyword">catch</span> (e: Exception) &#123;</span><br><span class="line">            e.printStackTrace()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">    <span class="comment">//移除屏障</span></span><br><span class="line">    <span class="meta">@SuppressLint</span>(<span class="string">"DiscouragedPrivateApi"</span>)</span><br><span class="line">    <span class="meta">@RequiresApi</span>(api = Build.VERSION_CODES.M)</span><br><span class="line">    <span class="function">fun <span class="title">removeSyncBarrier</span><span class="params">()</span> </span>&#123;</span><br><span class="line">        Log.i(TAG, <span class="string">"移除屏障"</span>)</span><br><span class="line">        <span class="keyword">try</span> &#123;</span><br><span class="line">            val queue: MessageQueue = handler.looper.queue</span><br><span class="line">            val method: Method = MessageQueue::<span class="class"><span class="keyword">class</span>.<span class="title">java</span>.<span class="title">getDeclaredMethod</span>(</span></span><br><span class="line">                "removeSyncBarrier",</span><br><span class="line">                Int::<span class="class"><span class="keyword">class</span>.<span class="title">javaPrimitiveType</span></span></span><br><span class="line"><span class="class">            )</span></span><br><span class="line"><span class="class">            <span class="title">method</span>.<span class="title">isAccessible</span> </span>= <span class="keyword">true</span></span><br><span class="line">            method.invoke(queue, token)</span><br><span class="line">        &#125; <span class="keyword">catch</span> (e: Exception) &#123;</span><br><span class="line">            e.printStackTrace()</span><br><span class="line">        &#125;</span><br><span class="line">    &#125;</span><br><span class="line"></span><br><span class="line">&#125;</span><br></pre></td></tr></table></figure></div><div class="popular-posts-header">相关文章推荐</div><ul class="popular-posts"><li class="popular-posts-item"><div class="popular-posts-title"><a href="/posts/d2b7ca20/" rel="bookmark">使用AIDL进程间通信</a></div></li><li class="popular-posts-item"><div class="popular-posts-title"><a href="/posts/ced7c86b/" rel="bookmark">使用Intent进程间通信</a></div></li><li class="popular-posts-item"><div class="popular-posts-title"><a href="/posts/cb9734d/" rel="bookmark">使用Messenger进程间通信</a></div></li><li class="popular-posts-item"><div class="popular-posts-title"><a href="/posts/f6108cad/" rel="bookmark">反射浅析</a></div></li><li class="popular-posts-item"><div class="popular-posts-title"><a href="/posts/b64e3989/" rel="bookmark">Android 系统启动流程 浅析</a></div></li></ul><div class="reward-container"><div></div><button onclick='var e=document.getElementById("qr");e.style.display="none"===e.style.display?"block":"none"'>打赏</button><div id="qr" style="display:none"><div style="display:inline-block"><img src="/images/wechatpay.png" alt="在雨季等你 微信支付"><p>微信支付</p></div><div style="display:inline-block"><img src="/images/alipay.png" alt="在雨季等你 支付宝"><p>支付宝</p></div></div></div><div><ul class="post-copyright"><li class="post-copyright-author"><strong>本文作者： </strong>在雨季等你</li><li class="post-copyright-link"><strong>本文链接：</strong> <a href="https://allens.icu/posts/a9cacf3c/" title="Android Handler 浅析">https://allens.icu/posts/a9cacf3c/</a></li><li class="post-copyright-license"><strong>版权声明： </strong>本博客所有文章除特别声明外，均采用 <a href="https://creativecommons.org/licenses/by-nc-sa/4.0/" rel="noopener" target="_blank"><i class="fab fa-fw fa-creative-commons"></i>BY-NC-SA</a> 许可协议。转载请注明出处！</li></ul></div><footer class="post-footer"><div class="post-tags"><a href="/tags/Android%E7%B3%BB%E7%BB%9F/" rel="tag"># Android系统</a></div><div class="post-nav"><div class="post-nav-item"><a href="/posts/66f4960a/" rel="prev" title="Activity 启动模式"><i class="fa fa-chevron-left"></i> Activity 启动模式</a></div><div class="post-nav-item"><a href="/posts/6a3e872a/" rel="next" title="Android 过渡动画 浅析">Android 过渡动画 浅析 <i class="fa fa-chevron-right"></i></a></div></div></footer></article></div><div class="comments" id="valine-comments"></div><script>window.addEventListener('tabs:register', () => {
    let { activeClass } = CONFIG.comments;
    if (CONFIG.comments.storage) {
      activeClass = localStorage.getItem('comments_active') || activeClass;
    }
    if (activeClass) {
      let activeTab = document.querySelector(`a[href="#comment-${activeClass}"]`);
      if (activeTab) {
        activeTab.click();
      }
    }
  });
  if (CONFIG.comments.storage) {
    window.addEventListener('tabs:click', event => {
      if (!event.target.matches('.tabs-comment .tab-content .tab-pane')) return;
      let commentClass = event.target.classList[1];
      localStorage.setItem('comments_active', commentClass);
    });
  }</script></div><div class="toggle sidebar-toggle"><span class="toggle-line toggle-line-first"></span> <span class="toggle-line toggle-line-middle"></span> <span class="toggle-line toggle-line-last"></span></div><aside class="sidebar"><div class="sidebar-inner"><ul class="sidebar-nav motion-element"><li class="sidebar-nav-toc">文章目录</li><li class="sidebar-nav-overview">站点概览</li></ul><div class="post-toc-wrap sidebar-panel"><div class="post-toc motion-element"><ol class="nav"><li class="nav-item nav-level-1"><a class="nav-link" href="#Handler的基本原理"><span class="nav-number">1.</span> <span class="nav-text">Handler的基本原理</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#消息是如何添加到队列的"><span class="nav-number">2.</span> <span class="nav-text">消息是如何添加到队列的</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#消息队列如何将消息排序"><span class="nav-number">3.</span> <span class="nav-text">消息队列如何将消息排序</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Handler的消息队列在哪创建的"><span class="nav-number">4.</span> <span class="nav-text">Handler的消息队列在哪创建的</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Loope有什么作用"><span class="nav-number">5.</span> <span class="nav-text">Loope有什么作用</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#一个线程有几个Looper"><span class="nav-number">6.</span> <span class="nav-text">一个线程有几个Looper</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#主线程什么时候执行preapre"><span class="nav-number">7.</span> <span class="nav-text">主线程什么时候执行preapre</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Handler内存泄露"><span class="nav-number">8.</span> <span class="nav-text">Handler内存泄露</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#消息队列没有消息时Handler如何挂起"><span class="nav-number">9.</span> <span class="nav-text">消息队列没有消息时Handler如何挂起</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Handler如何退出"><span class="nav-number">10.</span> <span class="nav-text">Handler如何退出</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#主线程能够Quit么？"><span class="nav-number">11.</span> <span class="nav-text">主线程能够Quit么？</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#为什么设计主线程不能被quit"><span class="nav-number">12.</span> <span class="nav-text">为什么设计主线程不能被quit</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#消息如何知道是由哪个Handler发送的？"><span class="nav-number">13.</span> <span class="nav-text">消息如何知道是由哪个Handler发送的？</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Handler如何确保线程安全的"><span class="nav-number">14.</span> <span class="nav-text">Handler如何确保线程安全的</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Message如何复用的"><span class="nav-number">15.</span> <span class="nav-text">Message如何复用的</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#为什么主线程loop不会导致ANR"><span class="nav-number">16.</span> <span class="nav-text">为什么主线程loop不会导致ANR</span></a></li><li class="nav-item nav-level-1"><a class="nav-link" href="#Handler同步屏障"><span class="nav-number">17.</span> <span class="nav-text">Handler同步屏障</span></a><ol class="nav-child"><li class="nav-item nav-level-2"><a class="nav-link" href="#同步屏障概念"><span class="nav-number">17.1.</span> <span class="nav-text">同步屏障概念</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#开启同步屏障"><span class="nav-number">17.2.</span> <span class="nav-text">开启同步屏障</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#同步屏障工作原理"><span class="nav-number">17.3.</span> <span class="nav-text">同步屏障工作原理</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#取消同步屏障"><span class="nav-number">17.4.</span> <span class="nav-text">取消同步屏障</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#GIF演示"><span class="nav-number">17.5.</span> <span class="nav-text">GIF演示</span></a></li><li class="nav-item nav-level-2"><a class="nav-link" href="#演示代码"><span class="nav-number">17.6.</span> <span class="nav-text">演示代码</span></a></li></ol></li></ol></div></div><div class="site-overview-wrap sidebar-panel"><div class="site-author motion-element" itemprop="author" itemscope itemtype="http://schema.org/Person"><img class="site-author-image" itemprop="image" alt="在雨季等你" src="/images/heard.jpg"><p class="site-author-name" itemprop="name">在雨季等你</p><div class="site-description" itemprop="description">分享是进步最快的方式</div></div><div class="site-state-wrap motion-element"><nav class="site-state"><div class="site-state-item site-state-posts"><a href="/archives/"><span class="site-state-item-count">23</span> <span class="site-state-item-name">日志</span></a></div><div class="site-state-item site-state-categories"><a href="/categories/"><span class="site-state-item-count">7</span> <span class="site-state-item-name">分类</span></a></div><div class="site-state-item site-state-tags"><a href="/tags/"><span class="site-state-item-count">6</span> <span class="site-state-item-name">标签</span></a></div></nav></div><div class="links-of-author motion-element"><span class="links-of-author-item"><a href="https://github.com/JiangHaiYang01" title="GitHub → https:&#x2F;&#x2F;github.com&#x2F;JiangHaiYang01" rel="noopener" target="_blank"><i class="github fa-fw"></i>GitHub</a> </span><span class="links-of-author-item"><a href="https://www.jianshu.com/u/142e8a9d9d71" title="简书 → https:&#x2F;&#x2F;www.jianshu.com&#x2F;u&#x2F;142e8a9d9d71" rel="noopener" target="_blank"><i class="heartbeat fa-fw"></i>简书</a> </span><span class="links-of-author-item"><a href="https://juejin.im/user/5ade713ef265da0b92653545/posts" title="掘金 → https:&#x2F;&#x2F;juejin.im&#x2F;user&#x2F;5ade713ef265da0b92653545&#x2F;posts" rel="noopener" target="_blank"><i class="heartbeat fa-fw"></i>掘金</a></span></div><div class="links-of-blogroll motion-element links-of-blogroll-block"><div class="links-of-blogroll-title"><i class="fa fa-history fa-" aria-hidden="true"></i> 近期文章</div><ul class="links-of-blogroll-list"><li><a href="/posts/51fd8696/" title="线程总结" target="_blank">线程总结</a></li><li><a href="/posts/dac154ea/" title="Jetpack之Navigation浅析" target="_blank">Jetpack之Navigation浅析</a></li><li><a href="/posts/6ef6837/" title="DataBinding 浅析" target="_blank">DataBinding 浅析</a></li><li><a href="/posts/1981ac2c/" title="文章总览" target="_blank">文章总览</a></li><li><a href="/posts/f6108cad/" title="反射浅析" target="_blank">反射浅析</a></li></ul></div></div><div class="back-to-top motion-element"><i class="fa fa-arrow-up"></i> <span>0%</span></div></div></aside><div id="sidebar-dimmer"></div></div></main><footer class="footer"><div class="footer-inner"><div class="copyright">&copy; 2018 – <span itemprop="copyrightYear">2020</span> <span class="with-love"><i class="fa fa-heart"></i> </span><span class="author" itemprop="copyrightHolder">在雨季等你</span></div></div></footer></div><script src="/lib/anime.min.js"></script><script src="//cdn.jsdelivr.net/npm/jquery@3/dist/jquery.min.js"></script><script src="//cdn.jsdelivr.net/gh/fancyapps/fancybox@3/dist/jquery.fancybox.min.js"></script><script src="//cdn.jsdelivr.net/npm/lozad@1/dist/lozad.min.js"></script><script src="/lib/velocity/velocity.min.js"></script><script src="/lib/velocity/velocity.ui.min.js"></script><script src="/js/utils.js"></script><script src="/js/motion.js"></script><script src="/js/schemes/pisces.js"></script><script src="/js/next-boot.js"></script><script>!function(){var e,t,o=document.getElementsByTagName("link");if(o.length>0)for(i=0;i<o.length;i++)"canonical"==o[i].rel.toLowerCase()&&o[i].href&&(e=o[i].href);t=e?e.split(":")[0]:window.location.protocol.split(":")[0],e||(e=window.location.href),!function(){var i=/([http|https]:\/\/[a-zA-Z0-9\_\.]+\.baidu\.com)/gi,o=e,r=document.referrer;if(!i.test(o)){var n="https"===String(t).toLowerCase()?"https://sp0.baidu.com/9_Q4simg2RQJ8t7jm9iCKT-xh_/s.gif":"//api.share.baidu.com/s.gif";r?(n+="?r="+encodeURIComponent(document.referrer),o&&(n+="&l="+o)):o&&(n+="?l="+o);var a=new Image;a.src=n}}(window)}()</script><script src="/js/local-search.js"></script><script src="//cdn.jsdelivr.net/npm/quicklink@1/dist/quicklink.umd.js"></script><script>window.addEventListener('load', () => {
      quicklink({
        timeout : 3000,
        priority: true,
        ignores : [uri => uri.includes('#'),uri => uri === 'https://allens.icu/posts/a9cacf3c/',]
      });
      });</script><script>NexT.utils.loadComments(document.querySelector('#valine-comments'), () => {
  NexT.utils.getScript('//unpkg.com/valine/dist/Valine.min.js', () => {
    var GUEST = ['nick', 'mail', 'link'];
    var guest = 'nick,mail,link';
    guest = guest.split(',').filter(item => {
      return GUEST.includes(item);
    });
    new Valine({
      el         : '#valine-comments',
      verify     : false,
      notify     : false,
      appId      : '6lUfyTDNhn0hAsWfRDnGKAV3-gzGzoHsz',
      appKey     : 'EsKctJGDiO5s9Dd5qQ4791DG',
      placeholder: "欢迎吐槽",
      avatar     : 'mm',
      meta       : guest,
      pageSize   : '10' || 10,
      visitor    : false,
      lang       : 'zh-cn' || 'zh-cn',
      path       : location.pathname,
      recordIP   : false,
      serverURLs : ''
    });
  }, window.Valine);
});</script></body></html><!-- rebuild by neat -->